home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1995 November / Macworld Nov ’95.toast / Developers / BoxMaker++ / BoxMaker++ ƒ / sources / boxmaker.cp next >
Encoding:
Text File  |  1995-06-14  |  20.3 KB  |  818 lines  |  [TEXT/KAHL]

  1. #include <Types.h>
  2. #include <Memory.h>
  3. #include <QuickDraw.h>
  4. #include <OSUtils.h>
  5. #include <ToolUtils.h>
  6. #include <Menus.h>
  7. #include <Packages.h>
  8. #include <StandardFile.h>
  9. #include <Traps.h>
  10. #include <Files.h>
  11. #include <Aliases.h>
  12. #include <AppleEvents.h>
  13. #include <GestaltEqu.h>
  14. #include <Processes.h>
  15. #include <Fonts.h>
  16. #include <OSEvents.h>
  17. #include <Resources.h>
  18. #include <Desk.h>
  19. #include <Errors.h>
  20.  
  21. #include "boxmaker constants.h"
  22.  
  23. #include "standardgetfile.h"
  24. #include "boxmakergetfile.h"
  25. #include "boxmaker.h"
  26.  
  27. const AEAddressDesc boxmaker::self = GetTargetToSelf();
  28.  
  29. const CursHandle boxmaker::theClock = GetCursor( watchCursor);
  30.  
  31. Boolean boxmaker::itsProcessable() const
  32. {
  33.     Boolean result = false;
  34.  
  35.     if( itsVisible() || passInvisibles)
  36.     {
  37.         //
  38.         // First check against the 'typs' resource, if one was present
  39.         //
  40.         result = matchesTypeList();
  41.  
  42.         if( result == true)
  43.         {
  44.             //
  45.             // We passed the 'typs' resource check; check against
  46.             // the 'tycr' resource.
  47.             //
  48.             result = matchesTypeCreatorPairs();
  49.         }
  50.     }
  51.     return result;
  52. }
  53.  
  54. void boxmaker::InitToolbox() 
  55. {    
  56.     InitGraf( &qd.thePort);
  57.     InitFonts();
  58.     InitWindows();
  59.     InitMenus();
  60.     TEInit();
  61.     InitDialogs( 0L);
  62.     InitCursor();
  63.     FlushEvents( everyEvent, 0);
  64.     MoreMasters();
  65.     MoreMasters();
  66. }
  67.  
  68. Boolean boxmaker::InitGlobals() 
  69. {
  70.     the_status    = shell_is_running;
  71.     gMainDialog    = NULL;
  72.  
  73.     long aLong;
  74.     return (Gestalt( gestaltAppleEventsAttr, &aLong) == noErr);
  75. }
  76.  
  77. void boxmaker::SetUpMenus()
  78. {
  79.     Handle theMenus = GetNewMBar( kMBarID);
  80.     
  81.     SetMenuBar( theMenus);
  82.  
  83.     MenuHandle theAppleMenu = GetMenuHandle( kAppleMenuID);
  84.     AppendResMenu( theAppleMenu, 'DRVR');
  85.     DrawMenuBar();
  86. }
  87. //
  88. //    This routine is called during startup to display a splash screen.
  89. //    
  90. //    This was recommend by the Blue Team HI person, John Sullivan, who
  91. //    feels that all apps should display something so that users can easily
  92. //    tell what is running, and be able to switch by clicking.  Thanks John!
  93. //
  94. void boxmaker::GetMainDialog()
  95. {
  96.     if( !gMainDialog)
  97.     {
  98.         gMainDialog = GetNewDialog( kSettingsDialogID,
  99.                                     &myDialogRecord, (WindowPtr)-1L);
  100.     }
  101. }
  102.  
  103. void boxmaker::ShowPreferences()
  104. {
  105.     if( gMainDialog != 0)
  106.     {
  107.         ShowWindow( gMainDialog);
  108.     }
  109. }
  110.  
  111. void boxmaker::ShowAbout()
  112. {
  113.     (void)Alert( kAboutAlertID, NULL);
  114. }
  115.  
  116. void boxmaker::DoAppleMenu( short itemID)
  117. {
  118.     if( itemID == 1)
  119.     {
  120.         ShowAbout();
  121.     } else {
  122.         Str255 itemStr;
  123.         GetMenuItemText( GetMenuHandle( kAppleMenuID), itemID, itemStr);
  124.         OpenDeskAcc( itemStr);
  125.     }
  126. }
  127.  
  128. void boxmaker::DoMenu( long retVal)
  129. {
  130. //    const short menuID = HiWord( retVal);
  131. //    const short itemID = LoWord( retVal);
  132.     const short menuID = retVal >> 16;
  133.     const short itemID = retVal & 0x00000FFFF;
  134.  
  135.     switch( menuID)
  136.     {
  137.         case kAppleMenuID:
  138.             DoAppleMenu( itemID);
  139.             break;
  140.             
  141.         case kFileMenuID:
  142.             switch( itemID)
  143.             {
  144.                 case kSelectFileItem:
  145.                     SelectFile();
  146.                     break;
  147.             
  148.                 case kPrefsItem:
  149.                     ShowPreferences();
  150.                     break;
  151.                     
  152.                 case kQuitItem:
  153.                     SendQuitToSelf();
  154.                     break;
  155.             }
  156.             break;
  157.         
  158.         default:
  159.             break;            
  160.     }
  161.     HiliteMenu( 0);
  162. }
  163.  
  164. void boxmaker::DoMouseDown()
  165. {
  166.     WindowPtr    whichWindow;
  167.     short whichPart = FindWindow( myEvent.where, &whichWindow);
  168.     switch( whichPart)
  169.     {
  170.         case inMenuBar:
  171.             DoMenu( MenuSelect( myEvent.where));
  172.             break;
  173.         
  174.         case inSysWindow:
  175.             SystemClick( &myEvent, whichWindow);
  176.             break;
  177.         
  178.         case inDrag:
  179.             DragWindow( whichWindow, myEvent.where, &qd.screenBits.bounds);
  180.             break;
  181.  
  182.         case inGoAway:
  183.             if( TrackGoAway( whichWindow, myEvent.where))
  184.             {
  185.                 HideWindow( whichWindow);
  186.             }
  187.             break;
  188.         //
  189.         // We do not handle zooming or resizing. It seems simple, but one would
  190.         // need to add a couple of virtual functions to make it practical, so
  191.         // we do not do it.
  192.         //
  193.         default:
  194.             break;
  195.     }
  196. }
  197.  
  198. void boxmaker::DoKeyDown()
  199. {
  200.     if( myEvent.modifiers & cmdKey)
  201.     {
  202.         DoMenu( MenuKey( (char)myEvent.message & charCodeMask));
  203.     }
  204. }
  205.  
  206. boxmaker::boxmaker( short dlogID) : boxmakergetfile( dlogID)
  207. {
  208.     InitToolbox();
  209.     if( !InitGlobals())
  210.     {
  211.         ErrorAlertQuit( kErrStringID, kCantRunErr, 0);
  212.     }
  213.     InitAEVTStuff();
  214.     SetUpMenus();
  215.     GetMainDialog();
  216.  
  217.     theCInfoPBRec.hFileInfo.ioNamePtr = theFSSpec.name;
  218.     
  219.     GetCurrentProcess( &myPSN);
  220.     time_of_next_cup_of_tea = TickCount() + kFirstSleepInterval;
  221. }
  222.  
  223. void boxmaker::run()
  224. {
  225.     Boolean canQuitNow = false;
  226.     while( !canQuitNow)
  227.     {
  228.         const short currentSleep = amInFront() ? kForeSleepValue : kBackSleepValue;
  229.     
  230.         gotEvent = WaitNextEvent( everyEvent, &myEvent, currentSleep, NULL);
  231.         //
  232.         // We use 'EventloopHook( …) && …' rather than '… && EventloopHook( …)'
  233.         // since we always want to call 'EventloopHook'.
  234.         //
  235.         canQuitNow = EventloopHook() && (the_status >= shell_is_finishing);
  236.     
  237.         if( gotEvent)
  238.         {
  239.             const int gotCmdKey = (myEvent.what == keyDown) && (myEvent.modifiers & cmdKey);
  240.     
  241.             if( !gotCmdKey && IsDialogEvent( &myEvent))
  242.             {
  243.                 short itemHit;
  244.                 DialogPtr theDialog;
  245.                 if( DialogSelect( &myEvent, &theDialog, &itemHit))
  246.                 {
  247.                     HandleDialogEvent( itemHit, theDialog);
  248.                 }
  249.             } else {
  250.                 switch( myEvent.what)
  251.                 {
  252.                     case kHighLevelEvent:
  253.                         DoHighLevelEvent();
  254.                         break;
  255.                         
  256.                     case mouseDown:
  257.                         DoMouseDown();
  258.                         break;
  259.                         
  260.                     case keyDown:
  261.                     case autoKey:
  262.                         DoKeyDown();
  263.                         break;
  264.                         
  265.                     default:
  266.                         break;
  267.                 }
  268.             }
  269.         }
  270.     }
  271. }
  272.  
  273. void boxmaker::InitAEVTStuff()
  274. {
  275.     OAPPPtr = NewAEEventHandlerProc( HandleOAPP);
  276.     ODOCPtr = NewAEEventHandlerProc( HandleODOC);
  277.     PDOCPtr = NewAEEventHandlerProc( HandlePDOC);
  278.     QuitPtr = NewAEEventHandlerProc( HandleQuit);
  279.  
  280.     OSErr aevtErr =
  281.  
  282.         AEInstallEventHandler( kCoreEventClass, kAEOpenApplication,
  283.                      OAPPPtr, (long)this, false)
  284.  
  285.         || AEInstallEventHandler( kCoreEventClass, kAEOpenDocuments,
  286.                     (AEEventHandlerUPP) ODOCPtr, (long)this, false)
  287.  
  288.         || AEInstallEventHandler( kCoreEventClass, kAEPrintDocuments,
  289.                     (AEEventHandlerUPP) PDOCPtr, (long)this, false)
  290.  
  291.         || AEInstallEventHandler( kCoreEventClass, kAEQuitApplication,
  292.                     (AEEventHandlerUPP) QuitPtr, (long)this, false);
  293.     FailErr( aevtErr);
  294. }
  295.  
  296. OSErr boxmaker::GotRequiredParams( AppleEvent *theAppleEvent)
  297. {
  298.     DescType    typeCode;
  299.     Size        actualSize;
  300.     OSErr        result;
  301.  
  302.     OSErr err = AEGetAttributePtr( theAppleEvent, keyMissedKeywordAttr,
  303.                     typeWildCard, &typeCode, NULL, 0, &actualSize);
  304.     
  305.     if( err == errAEDescNotFound)    // we got all the required params: all is ok
  306.     {
  307.         result = noErr;
  308.     } else if( err == noErr) {
  309.         result = errAEEventNotHandled;
  310.     } else {
  311.         result = err;
  312.     }
  313.     return result;
  314. }
  315. //
  316. //    This routine is the handler for the oapp (Open Application) event.
  317. //
  318. //    It first checks the number of parameters to make sure we got them all
  319. //    (even though we don't want any) and then calls the OpenApp userProc.
  320. //    Finally it checks to see if the caller wanted a reply & sends one,
  321. //    setting any error.
  322. //
  323. pascal OSErr HandleOAPP( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon)
  324. {
  325.     OSErr result = boxmaker::GotRequiredParams( theAppleEvent);
  326.  
  327.     boxmaker *me = (boxmaker *)handlerRefcon;
  328.     me->the_status = shell_is_OApped;
  329.     
  330.     me->OpenApp();        // pass it on to the app specific routine
  331.  
  332.     if( reply->dataHandle != NULL )    //    a reply is sought
  333.     {
  334.         const char error_message[] = "Opening";
  335.         const long error_length = sizeof( error_message) - 1;    // don't include zero terminator
  336.         result = AEPutParamPtr( reply, 'errs', 'TEXT', error_message, error_length);
  337.     }
  338.     return result;
  339. }
  340. //
  341. //    This routine is the handler for the quit (Quit Application) event.
  342. //
  343. //    It first checks the number of parameters to make sure we got them all 
  344. //    (even though we don't want any) and then calls the QuitApp userProc.
  345. //    Finally it checks to see if the caller wanted a reply & sends one,
  346. //    setting any error.
  347. //
  348. pascal OSErr HandleQuit( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon)
  349. {
  350.     OSErr result = boxmaker::GotRequiredParams( theAppleEvent);
  351.  
  352.     boxmaker *me = (boxmaker *)handlerRefcon;
  353.     
  354.     me->the_status = shell_is_quitting;
  355.  
  356.     if( reply->dataHandle != NULL )    //    a reply is sought
  357.     {
  358.         const char error_message[] = "Quitting";
  359.         const long error_length = sizeof( error_message) - 1;    // don't include zero terminator
  360.         result = AEPutParamPtr( reply, 'errs', 'TEXT', error_message, error_length);
  361.     }
  362.     return result;
  363. }
  364. //    
  365. //    This routine is the low level processing routine for both the 
  366. //    odoc (Open Document) and pdoc (Print Document) events.
  367. //
  368. //    This routine is the key one, since this is how we get the list of
  369. //    files/folders/disks to process.  The first thing to do is the get the
  370. //    list of files, and then make sure that's all the parameters (should be!).
  371. //    We then process each file in the list by calling _HandleOneDoc.
  372. //
  373. OSErr boxmaker::_HandleDocs( AppleEvent *theAppleEvent,
  374.                                 AppleEvent *reply, Boolean opening)
  375. {
  376.     AEDescList    docList;
  377.     long        itemsInList;
  378.  
  379.     OSErr result =
  380.         AEGetParamDesc( theAppleEvent, keyDirectObject, typeAEList, &docList);
  381.  
  382.     if( result == noErr)
  383.     {
  384.         result = GotRequiredParams( theAppleEvent);
  385.         if( result == noErr)
  386.         {
  387.             result = AECountItems( &docList, &itemsInList);
  388.         }
  389.     }
  390.     StartABunch( itemsInList, opening);
  391.     for( int index = 1; (index <= itemsInList) && (result == noErr); index++)
  392.     {
  393.         FSSpec        aFSSpec;
  394.         Size        actualSize;
  395.         AEKeyword    keywd;
  396.         DescType    typeCode;
  397.         
  398.         result = AEGetNthPtr( &docList, index, typeFSS, &keywd,
  399.                         &typeCode, (Ptr)&aFSSpec, sizeof( aFSSpec ), &actualSize);
  400.  
  401.         if( result == noErr)
  402.         {
  403.             //
  404.             // Fill in the volume reference number
  405.             // (it will not change during a recursive descent)
  406.             // But the different dropped items might be on different volumes,
  407.             // so we do it inside the 'itemsInList' loop.
  408.             // This is true since we do not 'follow aliases' during the descent.
  409.             // (implementing that would also mean having to check for endless loops,
  410.             // and thus would not be trivial)
  411.             //
  412.             theCInfoPBRec.hFileInfo.ioVRefNum = aFSSpec.vRefNum;
  413.     
  414.             result = _HandleOneDoc( aFSSpec, opening);
  415.         }
  416.     }
  417.     EndABunch( itemsInList, opening);
  418.     if( (the_status == shell_is_running) && opening)
  419.     {
  420.         //
  421.         //    The reason we also check for 'opening' is based on a recommendation
  422.         //    in the Apple event Registry which specifically states that you should
  423.         //    NOT quit on a 'pdoc' as the Finder will send you a 'quit' when it is
  424.         //    ready for you to do so.
  425.         //
  426.         the_status = shell_is_finishing;
  427.     }
  428.     const OSErr dispose_result = AEDisposeDesc( &docList);
  429.     if( result == noErr)
  430.     {
  431.         result = dispose_result;
  432.     }
  433.     return result;
  434. }
  435. //
  436. // _HandleOneDoc is called repeatedly by _HandleDocs
  437. //
  438. OSErr boxmaker::_HandleOneDoc( const FSSpec &aFSSpec, Boolean opening)
  439. {
  440.     OSErr result = GatherInfo( aFSSpec);
  441.  
  442.     if( result == noErr)
  443.     {
  444.         if( itsADirectory())
  445.         {
  446.             result = _HandleADir( theSubDirID, opening);
  447.         } else {
  448.             if( itsProcessable())
  449.             {
  450.                 SetUp_theFSSpec_from_theCInfoPBRec();
  451.                 OpenDoc( opening);
  452.             }
  453.         }
  454.     }
  455.     return result;
  456. }
  457. //
  458. // _HandleADir is the low-level recursive directory handler.
  459. //
  460. OSErr boxmaker::_HandleADir( long dirID, Boolean opening)
  461. {
  462.     OSErr result = noErr;
  463.  
  464.     if( itsVisible() || passInvisibles)
  465.     {
  466.         if( passFolders)
  467.         {
  468.             SetUp_theFSSpec_from_theCInfoPBRec();
  469.             OpenDoc( opening);
  470.         }
  471.         if( enterFolders && (itsVisible() || enterInvisibles))
  472.         {
  473.             SetUp_theFSSpec_from_theCInfoPBRec();
  474.             if( mayEnterFolder( opening))
  475.             {
  476.                 const char theAccessRights = getAccessRights();
  477.                 //
  478.                 // We must enter the folder whenever we have
  479.                 // either read or search privileges, or both
  480.                 //
  481.                 if( (theAccessRights & 3) == 3)
  482.                 {
  483.                     CantEnterFolder( opening);
  484.                 } else {
  485.                     EnterFolder( opening);
  486.                     int index = 1;
  487.                     do
  488.                     {
  489.                         result = GatherInfo( dirID, index);
  490.                         
  491.                         if( result == noErr)
  492.                         {
  493.                             if( itsADirectory())
  494.                             {
  495.                                 result = _HandleADir( theSubDirID, opening);
  496.                             } else {
  497.                                 if( itsProcessable())
  498.                                 {
  499.                                     SetUp_theFSSpec_from_theCInfoPBRec();
  500.                                     OpenDoc( opening);
  501.                                 }
  502.                             }
  503.                         }
  504.                         index += 1;
  505.                     } while( result == noErr);
  506.                     if( result == fnfErr)    // fnfErr simply indicates end of directory
  507.                     {
  508.                         result = noErr;
  509.                     }
  510.                     ExitFolder( opening);
  511.                 }
  512.             }
  513.         }
  514.     }
  515.     return result;
  516. }
  517. //
  518. //    This routine is the handler for the odoc (Open Document) event.
  519. //    
  520. //    The odoc event simply calls the common _HandleDocs routines, which will
  521. //    do the dirty work of parsing the AEVT & calling the userProcs.
  522. //
  523. pascal OSErr HandleODOC( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon)
  524. {
  525.     boxmaker *me = (boxmaker *)handlerRefcon;
  526.     return me->_HandleDocs( theAppleEvent, reply, true);
  527. }
  528. //
  529. //    This routine is the handler for the pdoc (Print Document) event.
  530. //
  531. //    The pdoc event like the odoc simply calls the common _HandleDocs routines
  532. //
  533. pascal OSErr HandlePDOC( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon )
  534. {
  535.     boxmaker *me = (boxmaker *)handlerRefcon;
  536.     return me->_HandleDocs( theAppleEvent, reply, false);
  537. }
  538. //
  539. //    This is the routine called by the main event loop, when a high level
  540. //    event is found.  Since we only deal with Apple events, and not other
  541. //    high level events, we just pass everything onto the AEM via AEProcessAppleEvent
  542. //
  543. void boxmaker::DoHighLevelEvent()
  544. {
  545.     FailErr( AEProcessAppleEvent( &myEvent));
  546. }
  547.  
  548. void boxmaker::SelectFile()
  549. {
  550.     doIt();
  551.     if( sfGood)
  552.     {
  553.         SendODOCToSelf( &sfFile);
  554.     }
  555. }
  556. //
  557. //    A quick & dirty error reporter
  558. //
  559. void boxmaker::ErrorAlert( short errorNo, short stringIndexID, short stringListID)
  560. {
  561.     Str255    param;
  562.     Str15    errorStr;
  563.  
  564.     NumToString( errorNo, errorStr);
  565.     GetIndString( param, stringListID, stringIndexID);
  566.     ParamText( param, errorStr, NULL, NULL);
  567.     (void)Alert( kErrorAlertID, NULL);
  568. }
  569.  
  570. AEAddressDesc boxmaker::GetTargetToSelf()
  571. {
  572.     ProcessSerialNumber    psn;
  573.  
  574.     psn.highLongOfPSN = 0;
  575.     psn.lowLongOfPSN  = kCurrentProcess;
  576.  
  577.     AEAddressDesc result;
  578.     (void)AECreateDesc( typeProcessSerialNumber, (Ptr)&psn,
  579.                                 sizeof(ProcessSerialNumber), &result);
  580.     return result;
  581. }
  582. //
  583. //    This routine is the low level routine used by the SendODOCToSelf
  584. //    routine.  It gets passed the list of files (in an AEDescList)
  585. //    to be sent as the data for the 'odoc', builds up the event
  586. //    and sends off the event.  
  587. //
  588. //    It is broken out from SendODOCToSelf so that a SendODOCListToSelf could
  589. //    easily be written and it could then call this routine - but that is left
  590. //    as an exercise to the reader.
  591. //
  592. void boxmaker::_SendDocsToSelf( AEDescList *aliasList)
  593. {
  594.     AppleEvent openDocAE;
  595.     OSErr err = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments,
  596.                 &self, kAutoGenerateReturnID, kAnyTransactionID, &openDocAE);
  597.  
  598.     if( err == noErr)
  599.     {
  600.         err = AEPutParamDesc( &openDocAE, keyDirectObject, aliasList);
  601.  
  602.         if( err == noErr)
  603.         {
  604.             //
  605.             //    Since we are sending to ourselves, there is no need for reply.
  606.             //
  607.             AppleEvent replyAE;
  608.             err = AESend( &openDocAE, &replyAE, kAENoReply | kAECanInteract,
  609.                                                 kAENormalPriority, 3600, NULL, NULL);
  610.             //
  611.             //    NOTE: Since we are not requesting a reply, we do not need to
  612.             //    need to dispose of the replyAE.  It is there simply as a 
  613.             //    placeholder.
  614.             //
  615.         }
  616.     }
  617.     err = AEDisposeDesc( &openDocAE);
  618. }
  619. //
  620. //    This is the routine called by SelectFile to send a single odoc to ourselves.
  621. //
  622. //    It calls the above low level routine to do the dirty work of sending the AEVT -
  623. //    all we do here is build a AEDescList of the file to be opened.
  624. //
  625. void boxmaker::SendODOCToSelf( FSSpec *theFileSpec)
  626. {
  627.     AEDescList    aliasList;
  628.  
  629.     const OSErr error = AECreateList( NULL, 0, false, &aliasList);
  630.  
  631.     if( error == noErr)
  632.     {
  633.         AliasHandle    aliasH;
  634.  
  635.         (void)NewAlias( NULL, theFileSpec, &aliasH);
  636.  
  637.         AEDesc aliasDesc;
  638.         aliasDesc.descriptorType = typeAlias;
  639.         aliasDesc.dataHandle     = (Handle)aliasH;
  640.         
  641.         (void)AEPutDesc( &aliasList, 0, &aliasDesc);
  642.  
  643.         DisposeHandle( (Handle)aliasH);
  644.  
  645.         _SendDocsToSelf( &aliasList);
  646.  
  647.         (void)AEDisposeDesc( &aliasList);
  648.     }
  649. }
  650.  
  651. Boolean boxmaker::amInFront() const
  652. {
  653.     ProcessSerialNumber frontProcess;
  654.     Boolean result = false;
  655.     if( GetFrontProcess( &frontProcess) == noErr)
  656.     {
  657.         (void)SameProcess( &myPSN, &frontProcess, &result);
  658.     }
  659.     return result;
  660. }
  661.  
  662. void boxmaker::SendQuitToSelf (void)
  663. {
  664.     AppleEvent quitAE;
  665.     OSErr error = AECreateAppleEvent( kCoreEventClass, kAEQuitApplication,
  666.                     &self, kAutoGenerateReturnID, kAnyTransactionID, &quitAE);
  667.  
  668.     if( error == noErr)
  669.     {
  670.         AppleEvent replyAE;
  671.         error = AESend( &quitAE, &replyAE, kAENoReply | kAECanInteract,
  672.                                         kAENormalPriority, 3600, NULL, NULL);
  673.         //
  674.         //    NOTE: Since we are not requesting a reply, we do not need to
  675.         //    need to dispose of the replyAE.  It is there simply as a 
  676.         //    placeholder.
  677.         //
  678.     }
  679.     (void)AEDisposeDesc( &quitAE);
  680. }
  681.  
  682. void boxmaker::errDebug( OSErr errno, unsigned char *errString)
  683. {
  684.     if( errno != noErr)
  685.     {
  686.         Str63 debugString;
  687.         NumToString( errno, *(Str255 *)&debugString);
  688.  
  689.         const int numberLength = debugString[ 0];
  690.         //
  691.         // the '+ 1' is for the colon after the error number
  692.         //
  693.         const int numFitting = 63 - (numberLength + 1);
  694.         const int errStringLength = errString[ 0];
  695.         const int numToCopy =
  696.             (errStringLength < numFitting) ? errStringLength : numFitting;
  697.  
  698.         unsigned char *firstafter = &debugString[ numberLength + 1];
  699.         *firstafter++ = ':';
  700.         const unsigned char *currentMessageChar = &errString[ 1];
  701.  
  702.         for( int i = 0; i < numToCopy; i++)
  703.         {
  704.             *firstafter++ = *currentMessageChar++;
  705.         }
  706.         debugString[ 0] = numberLength + 1 + errStringLength;
  707.         DebugStr( debugString);
  708.     }
  709. }
  710.  
  711. OSErr boxmaker::_GatherInfo( long theDirID)
  712. {
  713.     theCInfoPBRec.hFileInfo.ioDirID = theDirID;
  714.     //
  715.     // Note: According to IM, the field 'ioACUser' of the parameter block
  716.     // is unmodified when GetCatInfo is called for a non-network directory.
  717.     // (well, that is if one interprete a lack of an output arrow on page IV-153
  718.     // of IM-IV as meaning that the field in question is left untouched).
  719.     // However, with the arrival of 'File Manager extensions in a shared Environment'
  720.     // users can no longer assume that the field is unmodified, so one could suppose
  721.     // that GetCatInfo now _always_ returns something in that field. To be compatible
  722.     // with non-shared environments I set the field to a certainly good value before
  723.     // calling PBGetCatInfo. We use zero, which translates to 'everybody may do anything',
  724.     // which is a logical value in a non-shared environment.
  725.     // Also, I do not have the most recent header files, so I hack my way to the
  726.     // field in question.
  727.     //
  728.     ((char *)(&theCInfoPBRec))[ 31] = (char)0;
  729.     const OSErr result = PBGetCatInfoSync( &theCInfoPBRec);
  730.     
  731.     TimeForTea();
  732.     //
  733.     // ioDirID is both in- and output for GetCatInfo. If the item for which info
  734.     // was requested is a directory it is changed to the ID of that directory.
  735.     // In that case we have to set it back to the right value before we can do
  736.     // a 'SetCatInfo'. 'OpenDoc' assumes that theCInfoPBRec _can_ be used to set
  737.     // file information, so we reset it immediately. We do keep the ID of the item
  738.     // itself in 'theSubDirID'.
  739.     //
  740.     theSubDirID = theCInfoPBRec.hFileInfo.ioDirID;
  741.     theCInfoPBRec.hFileInfo.ioDirID = theDirID;
  742.     
  743.     return result;
  744. }
  745.  
  746. void boxmaker::StartABunch( long numTopLevelItems, Boolean opening)
  747. {
  748.     SetCursor( *theClock);
  749.     //
  750.     // Disable all menus:
  751.     //
  752.     Handle theMenuBar = GetMenuBar();
  753.     HLock( theMenuBar);
  754.  
  755.     const menulist *theList = (const menulist *)*theMenuBar;
  756.  
  757.     const int numMenus = theList->lastHandleOffset / 6;
  758.     
  759.     for( int i = 0; i < numMenus; i++)
  760.     {
  761.         const MenuHandle currentMenu = theList->theMenus[ i].theMenu;
  762.         DisableItem( currentMenu, 0);
  763.     }
  764.     DrawMenuBar();
  765.     DisposeHandle( theMenuBar);
  766. }
  767.  
  768. void boxmaker::EndABunch( long numTopLevelItems, Boolean opening)
  769. {
  770.     InitCursor();
  771.     //
  772.     // Enable all menus:
  773.     //
  774.     Handle theMenuBar = GetMenuBar();
  775.     HLock( theMenuBar);
  776.  
  777.     const menulist *theList = (const menulist *)*theMenuBar;
  778.  
  779.     const int numMenus = theList->lastHandleOffset / 6;
  780.     
  781.     for( int i = 0; i < numMenus; i++)
  782.     {
  783.         const MenuHandle currentMenu = theList->theMenus[ i].theMenu;
  784.         EnableItem( currentMenu, 0);
  785.     }
  786.     DrawMenuBar();
  787.     DisposeHandle( theMenuBar);
  788. }
  789.  
  790. void boxmaker::TimeForTea()
  791. {
  792.     //
  793.     // 950116: In an attempt to make dropboxes built with boxmaker++ more
  794.     // MultiFinder-friendly (anybody know a more modern name for that?)
  795.     // we call WaitNextEvent every now and then during a directory traversal.
  796.     // We do not want to get any further 'ODOC' events here, though, so we
  797.     // don't handle any events. This is only a rudimentary solution to the
  798.     // problem, but it does work (sort of).
  799.     //
  800.     if( TickCount() > time_of_next_cup_of_tea)
  801.     {
  802.         const Boolean inFront = amInFront();
  803.         const short currentSleep    = inFront ? kForeSleepValue    : kBackSleepValue;
  804.         const short currentInterval = inFront ? kForeSleepInterval : kBackSleepInterval;
  805.  
  806.         EventRecord junkEvent;
  807.         (void)WaitNextEvent( 0, &junkEvent, currentSleep, nil);
  808.  
  809.         if( inFront)
  810.         {
  811.             SetCursor( *theClock);
  812.         }
  813.         FlushEvents( mDownMask | mUpMask | keyDownMask | keyUpMask | autoKeyMask, 0);
  814.         
  815.         time_of_next_cup_of_tea = TickCount() + currentInterval;
  816.     }
  817. }
  818.